#!/opt/local/bin/perl

#########################################################################
#																		#
#	PorciBot	-	Perl IRC Bot										#
#																		#
#########################################################################
#																		#
#														Version 0.2.6.2 #
#																		#
#	Developed 2008 under the GNU General Public License (GPL)			#
#	Original Author: summel (eu.irc.freenode.org)						#
#	Other Main Developer(s):											#
#									SkuWo (eu.irc.furnet.org)			#
#																		#
#########################################################################

use Net::IRC;
use Data::Dumper;
use Date::Format;

#
#	Do NOT edit these lines! Edit the config file instead!
#

$userhost = '';				
$nickserv_password = '';	
$irc_server = '';
$irc_port = '';
$irc_nick = '';
$irc_name = '';
$irc_username = '';
$irc_channel = '';
$| = 1;						# This is neccesarry to write instantly to stdout/the logfile

require "./config";			#import the config file


#
# create the IRC object
#

my $irc = new Net::IRC;


print "Creating connection to IRC server...\n";

#
# Create a connection object.  You can have more than one "connection" per
# IRC object, but we'll just be working with one. More connections are planned for 1.5-
#

#
#	DO NOT EDIT! Edit the config file instead!
#

my $conn = $irc->newconn(
	Server 		=> $irc_server,
	Port		=> $irc_port,						
	Nick		=> $irc_nick,				
	Ircname		=> $irc_name,				
	Username	=> $irc_username
);


#
# We're going to add this to the conn hash so we know what channel we
# want to operate in.
#
$conn->{channel} = $irc_channel;						# Do NOT edit!
														# Edit the config file instead!


#
#  Here's some stuff to print at odd moments.
#  You can safely edit/add/delete the lines if you want.
#  This is stuff that the bot randomly says,
#  if his name is mentioned in a message
#

my @zippy = (
  "Oooooh... i'm spiky! are you spiky? ^_^",
  "hey there :)",
  "uhhh... heya! ^_^",
  "i'm fine and how are you? :)",
  "*giggles*",
  
  );


  
#
#	DO NOT EDIT BELOW THIS LINE
#
#	unless you know what you are doing!
# 


#
# what to do when connected
#

sub on_connect {

	# shift in our connection object that is passed automatically
	
	my $conn = shift;
  	print time2str("-%H:%M", time)." *** Joining $conn->{channel} ...\n";
	$conn->privmsg("nickserv", "identify $nickserv_password");
	
	# when we connect, join our channel and greet it
	
	$conn->join($conn->{channel});
	sleep(int(rand(5)));
	$conn->privmsg($conn->{channel}, 'Hi there! ^_^');
	$conn->{connected} = 1;
}

# What to do when someone leaves a channel the bot is on.

sub on_part {
    my ($self, $event) = @_;
    my ($channel) = ($event->to)[0];

	print time2str("-%H:%M", time)." *** $event->{nick} has left channel $channel\n";
}

#
# some output stuff
#

sub on_init {
    my ($self, $event) = @_;
    my (@args) = ($event->args);
    shift (@args);
    
    print time2str("-%H:%M", time)." *** @args\n";
}

#
# The end of MOTD (message of the day), numbered 376 signifies we've connect
#

$conn->add_handler('376', \&on_connect);


#
# stuff to do when somebody joins
#

sub on_join {
    my ($self, $event) = @_;
    my ($channel) = ($event->to)[0];

    print time2str("-%H:%M", time)." *** $event->{nick} ($event->{userhost}) has joined channel $channel\n";

	# get our connection object and the event object, which is passed
	# with this event automatically
	
	# this is the nick that just joined
	
	my $nick = $event->{nick};
	
	# say hello to the nick in public
	
	if ($nick ne $self->nick) {							# only if the nich is not our own nick
	sleep(int(rand(10)));								# wait 0-10 seconds. Seems to be more human.
	$conn->privmsg($conn->{channel}, "Hello, $nick!");
	}
}


#
# some stuff to say
#

sub on_public {


    my ($self, $event) = @_;
    my @to = $event->to;
    my ($nick, $mynick) = ($event->nick, $self->nick);
    my ($arg) = ($event->args);
    
     print time2str("-%H:%M", time)." <$nick> $arg\n";
    if ($arg =~ /$mynick/i) {                   # Say a Zippy quote if our nick
	$self->privmsg([ @to ], &pickrandom()); 	# appears in the message.
    }


# on an event, we get connection object and event hash
	
# this is what was said in the event
	
	my $text = $event->{args}[0];
	
# !info
	
	if ($text =~ /^\!info/) {
		
		$conn->privmsg($conn->{channel}, "Hi ^_^"); 
		sleep(2);
		$conn->privmsg($conn->{channel}, "I'm porci, the bot for #porcupine on eu.irc.furnet.org");
		sleep(2);
		$conn->privmsg($conn->{channel}, "I'm Version 0.2.6.2                 http://porcibot.bennid.de/");
			}
			
# !help
	
	if ($text =~ /^\!help/) {
		
		$conn->privmsg($event->{nick}, "Hi ^_^"); 
		sleep(2);
		$conn->privmsg($event->{nick}, "My functions are:");
		sleep(2);
		$conn->privmsg($event->{nick}, "!help (for this help obviously)");
		$conn->privmsg($event->{nick}, "!info !pet !hug");
		$conn->privmsg($event->{nick}, "(!pet and  !hug can be used with an argument)");
		sleep(2);
		$conn->privmsg($event->{nick}, "and if you are allowed to use it:");
		$conn->privmsg($event->{nick}, "!reset !leave !kick !op !deop !voice !devoice !topic !ban !unban");
		sleep(2);
		$conn->privmsg($event->{nick}, "(!op !voice !deop and !devoice can be used with an argument)");
		$conn->privmsg($event->{nick}, "(!kick and !topic must be used with an argument)");
		$conn->privmsg($event->{nick}, "(and if you want more detailed info of the functions there is !morehelp");
			}
			
# !morehelp
	
	if ($text =~ /^\!morehelp/) {
		
		$conn->privmsg($event->{nick}, "!pet pets a user (given as an argument). If no argument is given then porci will pet you."); 
		$conn->privmsg($event->{nick}, "!hug is the same as !pet except that porci will hug you/the argument and not pet.");
		sleep(2);
		$conn->privmsg($event->{nick}, "!reset reloads the bot (if its broken or the code got updated.*");
		$conn->privmsg($event->{nick}, "!leave shuts the bot down and only the person who initially run it can start it again. Maybe you want to use !reset instead. *)");
		sleep(2);
		$conn->privmsg($event->{nick}, "!op ops the user or (if given) the argument, if the argument is a valid username. *");
		$conn->privmsg($event->{nick}, "!deop removes op from the or (if given) the argument, if the argument is a valid username. *");
		sleep(2);
		$conn->privmsg($event->{nick}, "!voice gives voice to the user or (if given) the argument, if the argument is a valid username. *");
		$conn->privmsg($event->{nick}, "!devoice removes voice from the user or (if given) the argument, if the argument is a valid username. *");
		sleep(2);
		$conn->privmsg($event->{nick}, "!kick kicks the argument (if the argument given is a valid username). You can't kick yourself with !kick. *");
				$conn->privmsg($event->{nick}, "!topic changes the topic to the given argument. If no Argument is provided, the topic stays the same. *");
		sleep(2);
		$conn->privmsg($event->{nick}, "!ban bans the user given in the argument (if the argument is a valid username). *");
		$conn->privmsg($event->{nick}, "!unban unbans the user given in the argument (if the argument is a valid username) or unbans yourself, if no argument is given. *");
		$conn->privmsg($event->{nick}, " ");
		sleep(2);
		$conn->privmsg($event->{nick}, "The functions marked with a * require a special access level by the user who uses this command.");
					}
		

		
# !slap
		
	# regex the text to see if it begins with !slap
	if ($text =~ /^\!slap(?: (.+)|)$/) {
		# if so, check if there is an argument passed
		if ($1) {
			$self->privmsg([ @to ], &randomslap($1)); 
			}
		else {
			
		# if so, slap the argument :D
		$conn->privmsg($conn->{channel}, chr(01)."Action slaps $event->{nick}, because he/she's to stupid to use !slap. :P".chr(01));
		
		}
	}
		
# !pet subroutines :D
		
	# regex the text to see if it begins with !pet
	if ($text =~ /^\!pet(?: (.+)|)$/) {
		# if so, check if there is an argument passed
		if ($1) {
			$conn->privmsg($conn->{channel}, "*pets $1* :D");
			}
		else {
			
		# if so, pet the argument :D
		$conn->privmsg($conn->{channel}, "*pets $event->{nick}* :D");}
		
		}

			
# !hug subroutines ^_^	
		
	# regex the text to see if it begins with !hug
	if ($text =~ /^\!hug(?: (.+)|)$/) {
		# if so, check if there is an argument passed
		if ($1) {
			$conn->privmsg($conn->{channel}, chr(01)."Action hugs $1 ^_^ ".chr(01));
			}
		else {
			
		# if so, hug the argument :D
		$conn->privmsg($conn->{channel}, chr(01)."Action hugs $event->{nick} ^_^ ".chr(01));
		
		}	
	}
	
# !leave
		
	if ($text =~ /^\!leave/) {
		if ($event->userhost =~ $userhost) {  
			$self->quit("bye ^_^");
			print time2str("-%H:%M", time)." exited by user $event->{nick}\n";
			exit 0;
		}
	}



# !reset

	if ($text =~ /^\!reset/) {
		if ($event->userhost =~ /$userhost/) {  
			$self->quit("bye ^_^");
			print time2str("-%H:%M", time)." reset by user $event->{nick}\n";
			exec $^X, $0, @ARGV
		}
	}
	

# !kick
		
	if ($text =~ /^\!kick(?: (.+)|)$/) {
		if ($1) {
			$username = $1;
			print time2str("-%H:%M", time)." $event->{nick} kicked $username\n";
				if ($event->userhost =~ /$userhost/) {  
					$conn->privmsg("chanserv", "kick $conn->{channel} $username");}
				}
		else {
			if ($event->userhost =~ /$userhost/) {  
						print time2str("-%H:%M", time)." $event->{nick} tried to kick himself!\n";}
			}
	}
	
	

# !ban
		
	if ($text =~ /^\!ban(?: (.+)|)$/) {
		if ($1) {
			$username = $1;
			print time2str("-%H:%M", time)." $event->{nick} banned $username from $conn->{channel}\n";
				if ($event->userhost =~ /$userhost/) {  
					$conn->privmsg("chanserv", "ban $conn->{channel} $username");}
				}
		else {
			if ($event->userhost =~ /$userhost/) {  
						print time2str("-%H:%M", time)." $event->{nick} tried to ban himself from $conn->{channel}!\n";}
			}
	}	
	


# !unban
		
	if ($text =~ /^\!unban(?: (.+)|)$/) {
		if ($1) {
			$username = $1;
			print time2str("-%H:%M", time)." $event->{nick} unbanned $username from $conn->{channel}\n";
				if ($event->userhost =~ /$userhost/) {  
					$conn->privmsg($conn->{channel}, chr(01)."mode -b $conn->{channel} $1".chr(01));}
				}
		else {
			if ($event->userhost =~ /$userhost/) {  
						print time2str("-%H:%M", time)." $event->{nick} unbanned himself from $conn->{channel}!\n";
						$conn->privmsg($conn->{channel}, chr(01)."mode -b $event->{nick}".chr(01));}
			}
	}	
	
	


# !topic
		
	if ($text =~ /^\!topic(?: (.+)|)$/) {
		if ($1) {
			$topic = $1;
				if ($event->userhost =~ /$userhost/) {  
					$conn->privmsg("chanserv", "topic $conn->{channel} $topic");}
				}
		else {
			if ($event->userhost =~ /$userhost/) {  
						print time2str("-%H:%M", time)." $event->{nick} tried to change the topic to 0\n";}

			}
	}
	
	
	

# !op
		
	if ($text =~ /^\!op(?: (.+)|)$/) {
		if ($1) {
			$username = $1;
			print time2str("-%H:%M", time)." $event->{nick} gave op to $username\n";
				if ($event->userhost =~ /$userhost/) {  
					$conn->privmsg("chanserv", "op $conn->{channel} $username");}
				}
		else {
			if ($event->userhost =~ /$userhost/) {  
						print time2str("-%H:%M", time)." op for $event->{nick}\n";
					$conn->privmsg("chanserv", "op $conn->{channel} $event->{nick}");}
			}
	}



# !deop
		
	if ($text =~ /^\!deop(?: (.+)|)$/) {
		if ($1) {
			$username = $1;
			print time2str("-%H:%M", time)." $event->{nick} removed op from $username\n";
				if ($event->userhost =~ /$userhost/) {  
					$conn->privmsg("chanserv", "deop $conn->{channel} $username");}
				}
		else {
			if ($event->userhost =~ /$userhost/) {  
						print time2str("-%H:%M", time)." deop $event->{nick}\n";
					$conn->privmsg("chanserv", "deop $conn->{channel} $event->{nick}");}
			}
	}
	


# !voice
		
	if ($text =~ /^\!voice(?: (.+)|)$/) {
		if ($1) {
			$username = $1;
			print time2str("-%H:%M", time)." $event->{nick} gave voice to $username\n";
				if ($event->userhost =~ /$userhost/) {  
					$conn->privmsg("chanserv", "voice $conn->{channel} $username");}
				}
		else {
			if ($event->userhost =~ /$userhost/) {  
						print time2str("-%H:%M", time)." voice for $event->{nick}\n";
					$conn->privmsg("chanserv", "voice $conn->{channel} $event->{nick}");}
			}
	}




# !devoice
		
	if ($text =~ /^\!devoice(?: (.+)|)$/) {
		if ($1) {
			$username = $1;
			print time2str("-%H:%M", time)." $event->{nick} removed voice from $username\n";
				if ($event->userhost =~ /$userhost/) {  
					$conn->privmsg("chanserv", "devoice $conn->{channel} $username");}
				}
		else {
			if ($event->userhost =~ /$userhost/) {  
						print time2str("-%H:%M", time)." devoice for $event->{nick}\n";
					$conn->privmsg("chanserv", "devoice $conn->{channel} $event->{nick}");}
			}
	}

}



# answer to /msg

sub on_msg {
	my ($conn, $event) = @_;

	$conn->privmsg($event->{nick}, "Hi ^_^ I'm just a bot.");
	print time2str("-%H:%M", time)." :: privmsg from ".$event->{nick}."\n";
	sleep(3);
	$conn->privmsg($event->{nick}, "You can use !help or !info in the main channel (#porcupine)");
	
}



# Change our nick if someone stole it.

sub on_nick_taken {
    my ($self) = shift;

    $self->nick(substr($self->nick, -1) . substr($self->nick, 0, 8));
    print "Nick was already in use, trying another one!\n";
}



# Reconnect to the server when we die. (Seems to work not everytime)

sub on_disconnect {
	my ($self, $event) = @_;

	print "Disconnected from ", $event->from(), " (",
	      ($event->args())[0], "). Attempting to reconnect...\n";
	$self->connect();
}


# Look at the topic for a channel you join.

sub on_topic {
	my ($self, $event) = @_;
	my @args = $event->args();

	# Note the use of the same handler sub for different events.

	if ($event->type() eq 'notopic') {
	    print "No topic set for $args[1].\n";

	# If it's being done _to_ the channel, it's a topic change.
	
	} elsif ($event->type() eq 'topic' and $event->to()) {
	    print time2str("-%H:%M", time)." Topic change for ", $event->to(), ": $args[0]\n";

	} else {
	    print "The topic for $args[1] is \"$args[2]\".\n";
	}
}

#
# random stuff subroutine
#

sub pickrandom {   # Choose a random quote from the @zippy array.
	sleep(rand(5));
    return $zippy[ rand scalar @zippy ];
}

#
# random slap subroutine
#

sub randomslap {   # Returns a random slap from the @slap array.
	$username = shift;
	my @slap = (
		chr(01)."Action slaps $username around a bit with a pink 20\" rubber dildo. xD".chr(01),
		chr(01)."Action slaps $username around a bit with an imperial class stardestroyer".chr(01),
		chr(01)."Action slaps $username around a bit with a large trout".chr(01),
		chr(01)."Action hits $username with the Windows ME user guide".chr(01),
		chr(01)."Action hits $username with a Back Street Boys CD".chr(01),
		chr(01)."Action tortures $username with a set of Windows 3.11 floppies".chr(01),
	);
    return $slap[ rand scalar @slap ];
}

#
# prints a userlist
#

sub on_names {
    my ($self, $event) = @_;
    my (@list, $channel) = ($event->args);    # eat yer heart out, mjd!

    # splice() only works on real arrays. Sigh.
    ($channel, @list) = splice @list, 2;

    print "Users on $channel: @list\n";
}


#
# default output
#

sub default {

        # This is helpful to see what an event returns.  Data::Dumper will
        # recursively reveal the structure of any value
        my ($conn, $event) = @_;
        print Dumper($event);
}
        

# Yells about incoming CTCP PINGs.

sub on_ping {
    my ($self, $event) = @_;
    my $nick = $event->nick;

    $self->ctcp_reply($nick, join (' ', ($event->args)));
    print "*** CTCP PING request from $nick received\n";
}

# Gives lag results for outgoing PINGs.

sub on_ping_reply {
    my ($self, $event) = @_;
    my ($args) = ($event->args)[1];
    my ($nick) = $event->nick;

    $args = time - $args;
    print "*** CTCP PING reply from $nick: $args sec.\n";
}


# Display formatted CTCP ACTIONs.

sub on_action {
    my ($self, $event) = @_;
    my ($nick, @args) = ($event->nick, $event->args);

    print "* $nick @args\n";
}
        
        
# Handlers

$conn->add_handler('cping',  \&on_ping);
$conn->add_handler('crping', \&on_ping_reply);
$conn->add_handler('msg',    \&on_msg);
$conn->add_handler('chat',   \&on_chat);
$conn->add_handler('public', \&on_public);
$conn->add_handler('caction', \&on_action);
$conn->add_handler('join',   \&on_join);
$conn->add_handler('umode',   \&on_umode);
$conn->add_handler('part',   \&on_part);
$conn->add_handler('cdcc',   \&on_dcc);
$conn->add_handler('topic',   \&on_topic);
$conn->add_handler('notopic',   \&on_topic);

$conn->add_global_handler([ 251,252,253,254,302,255 ], \&on_init);
$conn->add_global_handler('disconnect', \&on_disconnect);
$conn->add_global_handler(376, \&on_connect);
$conn->add_global_handler(433, \&on_nick_taken);
$conn->add_global_handler(353, \&on_names);

		
$irc->start();
